﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Reactive.Linq;
using System.Windows.Markup;
using System.Diagnostics;

using alib.Enumerable;
using alib.Debugging;
using alib.Observable;

using agree.schema;

namespace agree
{
	using Titem = IChartObj<ChartSpan>;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	[ContentProperty("Tokens")]
	public sealed class VerifyCoverageLoader : m_observable<IActiveObj<Titem>, Titem>, IObserver<Titem>
	{
		public VerifyCoverageLoader(IRtParent parent, IActiveObj<Titem> toks)
			: base(parent, toks)
		{
		}


		///////////////////////////////////////////////////////////////////////
		///
		public IActiveObj<Titem> Tokens
		{
			get { return this.t0; }
			set { this.t0 = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////

		int cc;
		int[] coverage = null;

		void initialize_map()
		{
			TokenMappingLattice lat;
			OrthLexentLookup oll;

			if ((lat = this.FindTrace<TokenMappingLattice>()) != null)
				cc = lat.EndVertex.Level;
			else if ((oll = this.FindTrace<OrthLexentLookup>()) != null)
				cc = oll.span_count;
			else
				throw new Exception("trace not available");

			this.coverage = new int[cc];
		}

		protected override void start(IActiveObj<Titem> toks) { toks.Subscribe(this); }

		void IObserver<Titem>.OnNext(Titem po)
		{
			if (coverage == null)
				initialize_map();

			if (cc > 0)
			{
				var sp = po.ProximalContext;
				for (int itok = sp.StartIndex; itok <= sp.EndIndex; itok++)
					if (Interlocked.Increment(ref coverage[itok]) == 1 && Interlocked.Decrement(ref cc) == 0)
						break;
			}
			_item(po);
		}

		void IObserver<Titem>.OnCompleted()
		{
			if (cc > 0)
			{
				var item = (IString)P.SourceItem.FindTracePropertyValue(this);
				var err = new LexicalAnalysisException("'{0}': No coverage for the following token position(s): {1}",
								item != null ? item.Text : "(unknown)",
								coverage.SelectNotNull((c, ix) => c > 0 ? default(String) : ix.ToString()).StringJoin(", "));
				_error(err);
			}
			else
				_remove_task();
		}

		void IObserver<Titem>.OnError(Exception error) { _error(error); }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// previously, this monad exhibited the error of declaraing a property which is only valid after its own observable has been
	/// subscribed and completed. Since the semantics of this operation require examination of the whole sequence, the corrected
	/// behavior now is to materialize the entire source sequence, rather than to passthrough the partial "results." Note that since
	/// requesting the property triggers the work, it is also this monad's responsibility to buffer the source data for
	/// replay when the "real" subscription happens. As a side benefit of this, this observable can safely and correctly be
	/// subscribed multiple times. It is thread-safe for this.
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[ResubscribableMonad]
	public sealed class FindChartobjCoverage : m_scalar_base<IActiveObj<Titem>, IdentArray<Titem>>,
		ρρMinimalSpanCount
	{
		public FindChartobjCoverage(IRtParent parent, IActiveObj<Titem> monad)
			: base(parent, monad)
		{
			this.m_count = int.MinValue;
		}

		///////////////////////////////////////////////////////////////////////
		///
		int m_count;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int MinimalSpanCount
		{
			[TriggersMonadExecution]
			get
			{
				go();
				return this.m_count;
			}
			set { throw alib.not.valid; }
		}
		///
		///////////////////////////////////////////////////////////////////////

		IdentArray<Titem> m_arr;
		IdentArray<Titem> go()
		{
			IdentArray<Titem> _tmp;
			if ((_tmp = m_arr) == null)
			{
				lock (this)
				{
					if ((_tmp = m_arr) == null)
					{
						var arr = t0.ToArrayWait();

						int c, c_max = int.MinValue;
						for (int i = 0; i < arr.Length; i++)
							if ((c = arr[i].ProximalContext.EndIndex + 1) > c_max)
								c_max = c;

						this.m_count = c_max;
						this.m_arr = _tmp = new IdentArray<Titem>(this, arr);
					}
				}
			}
			return _tmp;
		}

		protected override IdentArray<Titem> start(IActiveObj<Titem> _)
		{
			return go();
		}
	};
}